﻿#include "stdafx.h"
#include "sbcs.h"
#include "sbctn.h"
#include "kstub_mutex.h"

//////////////////////////////////////////////////////////////////////////
// 심볼

#define MIN_SYM_COUNT		(8*64)
#define SYM_LOCK			K_LOCK(k_impl_sym.lock)
#define SYM_UNLOCK			K_UNLOCK(k_impl_sym.lock)

typedef struct kSymNode
{
	struct kSymNode*	sib;
	ksize_t				hash;
	const char*			key;
	kuint				value;
} kSymNode;

typedef struct kSymHash
{
	ksize_t				bucket;
	ksize_t				count;
	kSymNode**			nodes;
} kSymHash;

static struct kImplSymbol
{
	kuint				seq;

	kSymHash			hash;

	const char**		strs;
	ksize_t*			lens;
	kbyte*				isss;

	kLock				lock;
} k_impl_sym =
{
	0,
	{0, 0, NULL},
	NULL, NULL, NULL,
	KLOCK_INIT
};

static const char** _k_sym_data;

static kcham _k_sym_test_static(kuint n)
{
	kuint i = n / 8;
	kuint z = n - (i * 8);
	return (k_impl_sym.isss[i] & (1 << z)) != 0;
}

static void _k_sym_enable_static(kuint n)
{
	kuint i = n / 8;
	kuint z = n - (i * 8);
	k_impl_sym.isss[i] |= 1 << z;
}

static void _k_sym_disable_static(kuint n)
{
	kuint i = n / 8;
	kuint z = n - (i * 8);
	k_impl_sym.isss[i] &= ~(1 << z);
}

static void _k_sym_hash_test(void)
{
	ksize_t cnt = k_impl_sym.hash.count;
	ksize_t bkt = k_impl_sym.hash.bucket;

	if ((bkt >= 3 * cnt && bkt > K_MIN_HASH) ||
		(3 * bkt <= cnt && bkt < K_MAX_HASH))
	{
		ksize_t i, newbucket;
		kSymNode** newnodes;

		newbucket = k_primeclose(cnt);
		newbucket = K_CLAMP(newbucket, K_MIN_HASH, K_MAX_HASH);
		newnodes = k_new_zero(newbucket, kSymNode*);

		for (i = 0; i < bkt; i++)
		{
			kSymNode* node;
			kSymNode* next;
			ksize_t hashmask;

			for (node = k_impl_sym.hash.nodes[i]; node; node = next)
			{
				next = node->sib;
				hashmask = node->hash % newbucket;
				node->sib = newnodes[hashmask];
				newnodes[hashmask] = node;
			}
		}

		k_delete(k_impl_sym.hash.nodes);
		k_impl_sym.hash.nodes = newnodes;
		k_impl_sym.hash.bucket = newbucket;
	}
}

static kSymNode** _k_sym_hash_lookup(const char* str)
{
	ksize_t h = k_strhash(str);
	kSymNode** n = &k_impl_sym.hash.nodes[h % k_impl_sym.hash.bucket];
	kSymNode* nn;

	while ((nn = *n) != NULL)
	{
		if (nn->hash == h && k_streqv(nn->key, str))
			break;

		n = &nn->sib;
	}

	return n;
}

static kSymNode** _k_sym_hash_lookup_hash(const char* str, ksize_t* hash)
{
	ksize_t h = k_strhash(str);
	kSymNode** n = &k_impl_sym.hash.nodes[h % k_impl_sym.hash.bucket];
	kSymNode* nn;

	while ((nn = *n) != NULL)
	{
		if (nn->hash == h && k_streqv(nn->key, str))
			break;

		n = &nn->sib;
	}

	*hash = h;
	return n;
}

static void _k_sym_hash_add(const char* str, kuint sym)
{
	ksize_t h;
	kSymNode** n;
	kSymNode* nn;
	n = _k_sym_hash_lookup_hash(str, &h);
	nn = *n;

	if (nn)
	{
		// 대체 -> 안함
	}
	else
	{
		nn = k_new_1(kSymNode);
		nn->sib = NULL;
		nn->hash = h;
		nn->key = str;
		nn->value = sym;
		//
		*n = nn;
		k_impl_sym.hash.count++;
		//
		_k_sym_hash_test();
	}
}

static kuint _k_sym_hash_get(const char* str)
{
	kSymNode** n;
	kSymNode* nn;
	n = _k_sym_hash_lookup(str);
	nn = *n;
	return nn ? nn->value : 0;
}

void _k_sym_init(void)
{
	k_impl_sym.seq = 1;

	k_impl_sym.hash.count = 0;
	k_impl_sym.hash.bucket = K_MIN_HASH;
	k_impl_sym.hash.nodes = k_new_zero(K_MIN_HASH, kSymNode*);

	k_impl_sym.strs = k_new(MIN_SYM_COUNT, const char*);
	k_impl_sym.lens = k_new(MIN_SYM_COUNT, ksize_t);
	k_impl_sym.isss = k_new(MIN_SYM_COUNT / 8, kbyte);

	k_impl_sym.strs[0] = NULL;
	k_impl_sym.lens[0] = 0;
	k_impl_sym.isss[0] = 0xFF;

	_k_sym_data = k_impl_sym.strs;
}

void _k_sym_disp(void)
{
	ksize_t i;
	kSymNode* node;
	kSymNode* next;

	for (i = 1; i < k_impl_sym.seq; i++)
	{
		if (!_k_sym_test_static((kuint)i))
			k_delete(k_impl_sym.strs[i]);
	}

	k_delete(k_impl_sym.strs);
	k_delete(k_impl_sym.lens);
	k_delete(k_impl_sym.isss);

	for (i = 0; i < k_impl_sym.hash.bucket; i++)
	{
		for (node = k_impl_sym.hash.nodes[i]; node; node = next)
		{
			next = node->sib;
			k_delete(node);
		}
	}

	k_delete(k_impl_sym.hash.nodes);

	_k_sym_data = NULL;
}

static kuint _k_sym_new(const char* string, kcham isstatic)
{
	kuint n;

	if ((k_impl_sym.seq % MIN_SYM_COUNT) == 0)
	{
		n = k_impl_sym.seq + MIN_SYM_COUNT;
		k_impl_sym.strs = k_renew(k_impl_sym.strs, n, const char*);
		k_impl_sym.lens = k_renew(k_impl_sym.lens, n, ksize_t);
		k_impl_sym.isss = k_renew(k_impl_sym.isss, n / 8, kbyte);
		_k_sym_data = k_impl_sym.strs;
	}

	n = k_impl_sym.seq++;

	if (isstatic)
	{
		k_impl_sym.strs[n] = string;
		k_impl_sym.lens[n] = k_strlen(string);
		_k_sym_enable_static(n);
	}
	else
	{
		string = k_strdup(string);
		k_impl_sym.strs[n] = string;
		k_impl_sym.lens[n] = k_strlen(string);
		_k_sym_disable_static(n);
	}

	_k_sym_hash_add(string, n);

	return n;
}

static kuint _k_sym_try(const char* string)
{
	return _k_sym_hash_get(string);
}

static kuint _k_sym_get(const char* string, kcham isstatic)
{
	kuint n = _k_sym_hash_get(string);
	return n != 0 ? n : _k_sym_new(string, isstatic);
}

static kuint _k_sym_do(const char* string, kcham isstatic)
{
	kuint n;

	k_return_value_if_fail(string != NULL, 0);

	k_init();

	SYM_LOCK;
	n = _k_sym_get(string, isstatic);
	SYM_UNLOCK;

	return n;
}

static const char* _k_sym_intern(const char* string, kcham isstatic)
{
	kuint n;

	k_return_value_if_fail(string != NULL, NULL);

	k_init();

	SYM_LOCK;
	n = _k_sym_get(string, isstatic);
	string = k_impl_sym.strs[n];
	SYM_UNLOCK;

	return string;
}

static const char* _k_sym_both(const char* string, kuint* sym, kcham isstatic)
{
	kuint n;

	k_return_value_if_fail(string != NULL, NULL);

	k_init();

	SYM_LOCK;
	n = _k_sym_get(string, isstatic);
	string = k_impl_sym.strs[n];
	SYM_UNLOCK;

	if (sym)
		*sym = n;

	return string;
}

// 개수
kuint k_symcount(void)
{
	return (kuint)k_impl_sym.hash.count;
}

// 플렉
kuint k_symflx(const char* string)
{
	return _k_sym_do(string, FALSE);
}

// 스땍
kuint k_symstt(const char* string)
{
	return _k_sym_do(string, TRUE);
}

// 플렉
const char* k_syminflx(const char* string)
{
	return _k_sym_intern(string, FALSE);
}

// 슈뛕
const char* k_syminstt(const char* string)
{
	return _k_sym_intern(string, TRUE);
}

// 휅
const char* k_symbothflx(const char* string, kuint* sym)
{
	return _k_sym_both(string, sym, FALSE);
}

// 쉩
const char* k_symbothstt(const char* string, kuint* sym)
{
	return _k_sym_both(string, sym, TRUE);
}

// 값 <- 문자열+길이
kcham k_symvalue(kuint symbol, const char** str, ksize_t* len)
{
	k_init();

	if (symbol < k_impl_sym.seq)
	{
		if (str)
		{
			SYM_LOCK;
			*str = k_impl_sym.strs[symbol];

			if (len)
				*len = k_impl_sym.lens[symbol];

			SYM_UNLOCK;
		}

		return TRUE;
	}
	else
	{
		if (str)
		{
			*str = NULL;

			if (len)
				*len = 0;
		}

		return FALSE;
	}
}

// 문자열
const char* k_symstr(kuint symbol)
{
	const char* s;

	k_init();

	SYM_LOCK;
	s = symbol < k_impl_sym.seq ? k_impl_sym.strs[symbol] : NULL;
	SYM_UNLOCK;

	return s;
}

// 길이
ksize_t k_symlen(kuint symbol)
{
	ksize_t n;

	k_init();

	SYM_LOCK;
	n = symbol < k_impl_sym.seq ? k_impl_sym.lens[symbol] : 0;
	SYM_UNLOCK;

	return n;
}

// 편안해여
kuint k_symtry(const char* string)
{
	kuint n;

	k_return_value_if_fail(string != NULL, 0);

	k_init();

	SYM_LOCK;
	n = _k_sym_try(string);
	SYM_UNLOCK;

	return n;
}

// 편안할까
kuint k_symlet(const char* string, const char** ret)
{
	kuint n;

	k_return_value_if_fail(string != NULL, 0);

	k_init();

	SYM_LOCK;

	n = _k_sym_try(string);

	if (n && ret)
		*ret = k_impl_sym.strs[n];

	SYM_UNLOCK;

	return n;
}

// 문자열
const char* k_symunstr(kuint symbol)
{
	return symbol < k_impl_sym.seq ? k_impl_sym.strs[symbol] : NULL;
}

// 길이
ksize_t k_symunlen(kuint symbol)
{
	return symbol < k_impl_sym.seq ? k_impl_sym.lens[symbol] : 0;
}

// 값
kcham k_symunvalue(kuint symbol, const char** str, ksize_t* len)
{
	if (symbol < k_impl_sym.seq)
	{
		*str = k_impl_sym.strs[symbol];
		*len = k_impl_sym.lens[symbol];
		return TRUE;
	}
	else
	{
		*str = NULL;
		*len = 0;
		return FALSE;
	}
}

// 보여줘
void k_symprint(void)
{
	kuint i;

	/*
	k_mesg("=======================================================================\n");
	k_mesg(" seq    | stt | string                                                 \n");
	k_mesg("========|=====|========================================================\n");
	*/

	k_mesgf(" %-7s| %-3s | %-4s | %s\n", "seq", "stt", "len", "string");

	// CFS로 잠그는 건 좀 무린데...
	SYM_LOCK;

	for (i = 1; i < k_impl_sym.seq; i++)
	{
		// 문자열 길이는 55임
		k_mesgf(" %-7u|  %c  | %4u | %s\n", i, _k_sym_test_static(i) ? 'S' : '_',
			(kuint)k_impl_sym.lens[i], k_impl_sym.strs[i] ? k_impl_sym.strs[i] : "(null)");
	}

	k_mesgf("--- total symbols : %u---\n", k_impl_sym.seq - 1);

	SYM_UNLOCK;
}
